/*
 *  Copyright (C) 2011 Jaime Pavlich-Mariscal
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */



package cl.ucn.disc.biblio.refcluster;

import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.lang.StringUtils;


public class Reference implements Comparable<Reference> {

	public String ref;
	public String originalString;

	String year;
	String author;
	String journal;
	String volume;
	String page;
	String doi;

	public Sentence journalSentence = new Sentence();
	Sentence authorSentence = new Sentence();
	
	 
	Pattern yearPattern = Pattern.compile("[0-9][0-9][0-9][0-9]");
	Pattern authorPattern = Pattern.compile("([*][A-Za-z 0-9]+|[A-Za-z][A-Za-z0-9]+[ ]*[A-Za-z0-9]*)");
	Pattern journalPattern = Pattern.compile("[*]?[A-Za-z 0-9]+");;
	Pattern volumePattern = Pattern.compile("[Vv][0-9]*");
	Pattern pagePattern = Pattern.compile("[Pp][0-9]*");
	Pattern doiPattern = Pattern.compile("DOI [^$]+");



	public Reference() {
		
	}
	public Reference(String ref) throws InvalidReferenceException {
		setRef(ref);
	}
	public Reference(String author, String year, String journal, String volume, String page, String doi) {
		this();
		this.year = year;
		this.author = author;
		this.journal = journal;
		this.volume = volume;
		this.page = page;
		this.doi = doi;
		updateSentences();
		updateStrings();
	}
	private void updateStrings() {
		List<String> l = new ArrayList<String>();
		l.add(author);
		l.add(year);
		l.add(journal);
		l.add(volume);
		l.add(page);
		l.add(doi);
		ref = StringUtils.join(l,",");
		ref = ref.replaceAll(",+", ","); // Remove consecutive commas
		ref = ref.replaceAll(",$", ""); // Remove commas at the end
		originalString = ref;
		
	}
	private void updateSentences() {
		if (author != null) {
			authorSentence.initialize(author);
		}
		if (journal != null) {
			journalSentence.initialize(journal);
		}
		
	}
	public String getYear() {
		return year;
	}
	public String getAuthor() {
		return author;
	}
	public String getJournal() {
		return journal;
	}
	public String getVolume() {
		return volume;
	}
	public String getPage() {
		return page;
	}
	
	public String getDoi() {
		return doi;
	}
	public void setRef(String str) throws InvalidReferenceException {
		this.originalString = str;
		this.ref = prepareRefString(str);
		String[] fields = str.split(",");
		for (int i=0; i<fields.length; i++ ) {
			fields[i] = fields[i].trim();
		}
		if (fields.length < 3) {
			throw new InvalidReferenceException();
		}
		int col=0;
		
		if (match(fields,col,authorPattern)) {
			author= fields[col];
//			authorSentence.initialize(author);
			col++;
		} else {
			author = null;
		}
		
		if (match(fields,col,yearPattern)) {
			year = fields[col];
			col++;
		} else {
			year = null;
		}
		
		if (match(fields,col,journalPattern) && !match(fields,col,volumePattern) && !match(fields,col,pagePattern)) {
			journal = fields[col];
//			journalSentence.initialize(journal);
			col++;
		} else {
			journal = null;
		}
		if (match(fields,col,volumePattern)) {
			volume = fields[col];
			col++;
		} else {
			volume = null;
		}
		
		// FIXME There are some pages that do not match the format P<number>. 
		// For example: BIANCHI G,2000,IEEE J SEL AREA COMM,V18,UNSP 535547
		if (match(fields,col,pagePattern)) {
			page = fields[col];
			col++;
		} else {
			page = null;
		}
		
		if (match(fields,col,doiPattern)) {
			doi = fields[col];
			col++;
		} else {
			doi = null;
		}
		updateSentences();
	}
	
	private String prepareRefString(String str) {
		
		return str.trim().toUpperCase().replaceAll("\\s+", " ").replaceAll(",\\s", ",");
	}
	private boolean match(String[] strArray, int col, Pattern p) {
		if (col < strArray.length) {
			String str = strArray[col];
			str = str.trim();
			Matcher m = p.matcher(str);
			return m.find() && m.group().equals(str);
		}
		return false;
	}

	@Override
	public String toString() {
		return ref;
	}
//	@Override
//	public String toString() {
//		StringBuilder builder = new StringBuilder();
//		builder.append("Ref [");
//		if (ref != null)
//			builder.append("ref=").append(ref).append(", ");
//		if (author != null)
//			builder.append("author=").append(author).append(", ");
//		if (journal != null)
//			builder.append("journal=").append(journal).append(", ");
//		if (page != null)
//			builder.append("page=").append(page).append(", ");
//		if (volume != null)
//			builder.append("volume=").append(volume).append(", ");
//		if (year != null)
//			builder.append("year=").append(year);
//		builder.append("]");
//		return builder.toString();
//	}
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((ref == null) ? 0 : ref.hashCode());
		return result;
	}
	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Reference other = (Reference) obj;
		if (ref == null) {
			if (other.ref != null)
				return false;
		} else if (!ref.equals(other.ref))
			return false;
		return true;
	}
	@Override
	public int compareTo(Reference o) {
		return ref.compareTo(o.ref);
	}
	
}
